package org.sg20.utils;
import java.util.Arrays;
import java.util.Comparator;

public class LpaDotVal {

    private Integer[] labelSet; //可能取值的标签的集合 本次实验为0~n-1
    public double[] probabilities; //各个标签的取值概率
    //之后也可以考虑以字符串作为标签,用字典存储标签和概率的映射关系


    private final LabelComparator labelComparator=new LabelComparator();

    /**
     * 根据标签概率对标签进行从大往小排序
     */
    private class LabelComparator implements Comparator<Integer>{

        @Override
        public int compare(Integer o1, Integer o2) {
            double diff=probabilities[o1]-probabilities[o2];
            if(diff>0) return -1;
            else if(diff==0) return  0;
            else return 1;
        }
    }

    /**
     * 指定该点可能取值的标签总数
     * 初始时所有标签取值概率都为0
     */
    public LpaDotVal(int labelNum) {
        this.probabilities = new double[labelNum]; //double数组默认值为0
        this.initLabels(labelNum);
    }

    private void initLabels(int labelNum){
        this.labelSet =new Integer[labelNum];
        for(int i=0;i<labelNum;i++) this.labelSet[i]=i;
    }

    /**
     * 指定该点可能取值的标签总数,和该点的初始标签
     * 则初始标签概率为1,其它为0
     */
    public LpaDotVal(int labelNum, int label) {
        this.probabilities = new double[labelNum]; //double数组默认值为0
        this.probabilities[label] = 1;
        this.initLabels(labelNum);
    }

    public LpaDotVal(double[] probabilities){
        this.probabilities=probabilities;
        this.initLabels(probabilities.length);
    }

    /**
     * @return [pr0, pr1, ...]"
     */
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[").append(String.format("%.3f",this.probabilities[0]));
        for (int i = 1; i < this.probabilities.length; i++) {
            sb.append(",").append(String.format("%.3f",this.probabilities[i]));
        }
        sb.append("]");
        return sb.toString();
    }

    /**
     * 获取该点的标签
     *
     * @return 以概率最大的作为标签
     */
    public int getLabel() {
        int maxIndex = 0;
        double maxProbability = 0;
        for (int i = 0; i < this.probabilities.length; i++) {
            if (this.probabilities[i] > maxProbability) {
                maxProbability = this.probabilities[i];
                maxIndex = i;
            }
        }
        return maxIndex;
    }

    /**
     * 多标签传播中对节点所带标签的权重进行归一化
     */
    public void normalization(int[] labels){
        if(labels==null) return;//空标签无法进行归一化
        double[] tempPrs=new double[labels.length];
        double sum=0;
        //取得有关标签的概率
        for(int i=0;i<labels.length;i++){
            int label=labels[i];
            double pr=probabilities[label];
            tempPrs[i]=pr;
            sum+=pr;
        }

        //将无关标签概率置0
        for(int i=0;i<probabilities.length;i++){
            this.probabilities[i]=0;
        }

        //对有关标签概率进行归一化
        for(int i=0;i<labels.length;i++){
            int label=labels[i];
            probabilities[label]=tempPrs[i]/sum;
        }
    }

    /**
     * 多标签传播算法中获取该点的标签
     * @param k 标签传播的个数
     * @return 概率前k大的非零的标签
     */
    public int[]getLabel(int k){
        int[] res;
        if(k==1) {
            res=new int[1];
            res[0]=getLabel();
        }
        else{
            Arrays.sort(labelSet,labelComparator);
            //获取前k大的非0标签个数
            int cnt=0;
            for(int i=0;i<k;i++){
                if(probabilities[labelSet[i]]==0)break;
                cnt++;
            }
            if(cnt==0)res=null; //空标签
            else {
                //返回前k大的标签
                res=new int[k];
                for(int i=0;i<k;i++) res[i]=labelSet[i];
            }
        }
        return res;
    }


    public void addProb(int label, double pr) {
        this.probabilities[label] += pr;
    }
}
